﻿using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;

namespace EncryptDecrypt
{
    public class AzureTableKeyGenerator
    {
        private X509Certificate2 cert;

        public AzureTableKeyGenerator(string thumbprint, StoreLocation storeLocation = StoreLocation.LocalMachine, StoreName storeName = StoreName.My)
        {
            this.cert = AzureTableCryptoKeyStore.GetCertificate(thumbprint, storeLocation: storeLocation, storeName: storeName);
        }

        public AzureTableKeyGenerator(X509Certificate2 cert)
        {
            this.cert = cert;
        }

        public void CreateNewKey(CloudStorageAccount storageAccount, int versionNumber)
        {
            //Create the key
            SymmetricKey newKeySet = CreateNewAESSymmetricKeyset();
            newKeySet.Version = versionNumber;

            //Create the table
            CloudTableClient client = storageAccount.CreateCloudTableClient();
            client.CreateTableIfNotExist(SymmetricKeyDataServiceContext.TableName);

            //Save the new row
            SymmetricKeyDataServiceContext ctx = new SymmetricKeyDataServiceContext(storageAccount);
            ctx.SaveSymmetricKey(newKeySet);
        }

        /// <summary>
        /// Creates a symmetric key.  See this link for more information behind the numbers
        /// http://blogs.msdn.com/b/shawnfa/archive/2006/10/09/the-differences-between-rijndael-and-aes.aspx
        /// </summary>
        /// <returns></returns>
        private SymmetricKey CreateNewAESSymmetricKeyset()
        {
            if (cert == null)
            {
                throw new InvalidOperationException("Unable to create new AES keyset; Certificate not loaded.");
            }

            byte[] symmKey, iv;

            using (AesManaged aes = new AesManaged())
            {
                aes.GenerateIV();
                aes.GenerateKey();

                symmKey = aes.Key;
                iv = aes.IV;
                aes.Clear();
            }

            // Encrypt the Symmetric Key for storage
            byte[] encryptedKey = EncryptRSA(symmKey, cert);

            //Verify that we can decrypt it 
            byte[] decryptedKey = DecryptRSA(encryptedKey, cert);

#if DEBUG
            if (symmKey.Length != decryptedKey.Length)
            {
                throw new ApplicationException("Decrypted key length differs from original key");
            }

            for (int i = 0; i < decryptedKey.Length; i++)
            {
                if (symmKey[i] != decryptedKey[i])
                {
                    throw new ApplicationException("Decrypted key differs from original key at byte " + i);
                }
            }
#endif

            SymmetricKey symmKeySet = new SymmetricKey()
            {
                iv = iv,
                Key = encryptedKey,
                CertificateThumbprint = cert.Thumbprint
            };


            return symmKeySet;
        }

        private byte[] DecryptRSA(byte[] b, X509Certificate2 cert)
        {
            RSACryptoServiceProvider RSA = (RSACryptoServiceProvider)cert.PrivateKey;
            byte[] decrypt = RSA.Decrypt(b, true);
            return decrypt;
        }

        private byte[] EncryptRSA(byte[] b, X509Certificate2 cert)
        {
            RSACryptoServiceProvider RSA = (RSACryptoServiceProvider)cert.PublicKey.Key;
            var encrypt = RSA.Encrypt(b, true);
            return encrypt;
        }

        //private byte[] CreateCryptograhicKey(int length)
        //{
        //    using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
        //    {
        //        byte[] key = new byte[length];

        //        crypto.GetBytes(key);

        //        return key;
        //    }
        //}
    }
}
